package com.guojiang.guoaicodemother.ai;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.guojiang.guoaicodemother.ai.tools.*;
import com.guojiang.guoaicodemother.exception.BusinessException;
import com.guojiang.guoaicodemother.exception.ErrorCode;
import com.guojiang.guoaicodemother.model.enums.CodeGenTypeEnum;
import com.guojiang.guoaicodemother.service.ChatHistoryService;
import dev.langchain4j.community.store.memory.chat.redis.RedisChatMemoryStore;
import dev.langchain4j.data.message.ToolExecutionResultMessage;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.service.AiServices;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;
import java.util.concurrent.TimeUnit;

/**
 * AI 代码生成服务工厂
 */
@Configuration
@Slf4j
public class AiCodeGeneratorServiceFactory {

    @Resource
    private ChatModel chatModel;

    @Resource
    private StreamingChatModel openAiStreamingChatModel;

    @Resource
    private StreamingChatModel reasoningStreamingChatModel;

    @Resource
    private RedisChatMemoryStore redisChatMemoryStore;

    @Resource
    private ChatHistoryService chatHistoryService;

    @Resource
    private ToolManager toolManager;

    /**
     * AI 服务实例 - Caffeine本地缓存
     * 缓存策略：
     * - 最大缓存 1000 个实例
     * - 写入后 30 分钟过期
     * - 访问后 10 分钟过期
     */
    private final Cache<String, AiCodeGeneratorService> serviceCache = Caffeine.newBuilder()
            .maximumSize(1000)
            // 设置或修改后，30 分钟内不访问则移除
            .expireAfterWrite(30, TimeUnit.MINUTES)
            // 访问之后，10 分钟过期
            .expireAfterAccess(Duration.ofMinutes(10))
            .removalListener((key, value, cause) -> {
                log.debug("AI 服务实例被移除，：缓存键：{}, 原因：{}", key, cause);
            })
            .build();

    /**
     * 根据 AppId 获取 AiService，实现对话记忆隔离
     *
     * @param appId
     * @return
     */
    public AiCodeGeneratorService getAiCodeGeneratorService(long appId) {
        // 默认构建缓存 Key 加入 HTML 生成模式，实际上没啥用
        return getAiCodeGeneratorService(appId, CodeGenTypeEnum.HTML);
    }

    /**
     * 根据 AppId 获取 AiService，实现对话记忆隔离
     *
     * @param appId
     * @param codeGenTypeEnum 生成类型
     * @return
     */
    public AiCodeGeneratorService getAiCodeGeneratorService(long appId, CodeGenTypeEnum codeGenTypeEnum) {
        String cacheKey = buildCacheKey(appId, codeGenTypeEnum);
        return serviceCache.get(cacheKey, k -> createAiCodeGeneratorService(appId, codeGenTypeEnum));
    }

    /**
     * 根据 AppId 创建 AiService，实现对话记忆隔离
     *
     * @param appId
     * @param codeGenTypeEnum 生成类型
     * @return
     */
    private AiCodeGeneratorService createAiCodeGeneratorService(long appId, CodeGenTypeEnum codeGenTypeEnum) {
        log.info("为 AppId；{} 创建 AI 服务实例中...", appId);
        MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()
                // 根据 AppId 创建 chatMemory
                .id(appId)
                .chatMemoryStore(redisChatMemoryStore)
                .maxMessages(15)
                .build();
        // 从数据库中加载对话历史到对话记忆中
        chatHistoryService.loadChatHistoryToMemory(appId, chatMemory);
        return switch (codeGenTypeEnum) {
            // Vue 工程项目生成模式
            case VUE_PROJECT -> AiServices.builder(AiCodeGeneratorService.class)
                    .chatModel(chatModel)
                    // 指定使用自定义的 Bean 流式推理模型
                    .streamingChatModel(reasoningStreamingChatModel)
                    // 指定 chatMemory，实现对话记忆隔离
                    .chatMemoryProvider(memoryId -> chatMemory)
                    .tools(toolManager.getAllTools())
                    // 处理工具调用错误问题，告诉 AI 这里没有这样的工具
                    .hallucinatedToolNameStrategy(toolExecutionRequest -> ToolExecutionResultMessage.from(
                            toolExecutionRequest,
                            "Error: there is no tool called "
                                    + toolExecutionRequest.name()
                    ))
                    .build();
            // HTML、多文件生成模式
            case HTML, MULTI_FILE -> AiServices.builder(AiCodeGeneratorService.class)
                    .chatModel(chatModel)
                    .streamingChatModel(openAiStreamingChatModel)
                    // 指定 chatMemory，实现对话记忆隔离
                    .chatMemory(chatMemory)
                    .build();
            // 其他模式
            default -> throw new BusinessException(ErrorCode.PARAMS_ERROR,
                    "不支持的生成类型：" + codeGenTypeEnum.getText());
        };
    }

    /**
     * 构造缓存 Key
     *
     * @param appId
     * @param codeGenTypeEnum
     * @return
     */
    private String buildCacheKey(long appId, CodeGenTypeEnum codeGenTypeEnum) {
        return appId + "_" + codeGenTypeEnum.getText();
    }

    /**
     * 创建 AI 代码生成器服务（弃用）
     *
     * @return
     */
    @Deprecated
    public AiCodeGeneratorService aiCodeGeneratorService() {
//        return AiServices.builder(AiCodeGeneratorService.class)
//                .chatModel(chatModel)
//                .streamingChatModel(openAiStreamingChatModel)
//                .build();
        return getAiCodeGeneratorService(0);
    }
}
